package Thread;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ThreadCreateAndSynchronize {
    //线程同步
    public static void main(String[] args) {
        //1.同步代码块锁
        /*
        synchronized (obj){
            obj引用数据类型
            可以改变obj的值 但是不能改变obj的引用 obj指向的对象关联了一个同步监视器 改变引用新对象的同步监视器不一样
            String Integer 最好不要用 一改就是改地址 因为在常量池里
            建议使用final 作为锁对象
        }
        执行过程: 1.thread1 执行代码块 检查对象的监视器是否上锁 如果没上锁 上锁之后(对这个对象上锁 所有对该对象的操作都无法执行)进入代码块
                2.如果在代码块里sleep()了 会阻塞 线程切换 但是sleep不释放锁
                3.thread2 执行代码块 监视器上锁 执行不了 阻塞 线程切换
                4.thread1 拿到cpu 继续执行 执行后释放锁 thread2执行1

        */
        //2.同步方法锁
        /*
        public synchronized void lock(){
            锁的是this对象(实现Runnable接口 Thread用的是同一个对象)
            其他的synchronized方法都不能用了
            list=Collections.synchronizedList(list);用的同步代码块
            静态方法是整个Class 所有的实例都是同一个锁
            缺点:不能主动释放锁
                读写都要加锁
        };
        */

        //3.Lock锁:ReentrantLock ReentrantReadWriteLock
        /*
        Lock lock=new ReentrantLock();
        AQS核心 维持一个等待队列 非公平锁 一起抢
        lock.lock();  //拿不到锁一直等待 可重入锁 会计数  是自己仍能进 默认非公平锁性能高(带参true为公平锁)
        lock.tryLock(time);// 尝试拿锁 拿不到false
        lock.lockInterruptibly();  //拿锁 直到线程被打断
        lock.unlock();//放在finally里  异常不会解锁
        */
        /*
        ReentrantReadWriteLock lock=new ReentrantReadWriteLock();//非公平 可重入 分为读锁和写锁
        //无论几个 都是同一把锁
        Lock readLock=lock.readLock();   //读写互斥
        Lock writeLock = lock.writeLock();
        */

        //4.volatile+CAS(Compare And Set) 无锁化方案-lock
        //4.1volatile
        /*
        volatile保证在多个线程中 变量的可见性
        println实现了synchronized synchronized可以保证可见性

        如果用while(flag)来控制线程的结束 不行
        JMM中规定所有的变量都存储在主内存（Main Memory）中，每条线程都有自己的工作内存（Work Memory），
        线程的工作内存中保存了该线程所使用的变量的从主内存中拷贝的副本。线程对于变量的读、写都必须在工作内存中进行，而不能直接读、写主内存中的变量。
        同时，本线程的工作内存的变量也无法被其他线程直接访问，必须通过主内存完成。
        如果一个线程修改了flag  其他线程的工作区中的flag仍不变

        volatile 实现可见性 跳过缓存直接在内存中读写
        当对volatile变量执行写操作后，JMM会把工作内存中的最新变量值强制刷新到主内存
        写操作会导致其他线程中的缓存无效
         */
        //线程安全三要素:原子性 有序性 可见性 lock synchronized 三个都能保证 volatile不能保证原子性
        //可见性  多个线程可见同一个变量
        //原子性  要么都成功 要么都失败
        //有序性  把无关的语句同时执行(指令重排序) volatile禁止指令重排 (内存屏障 不能把后面的语句重排序到内存屏障之前的位置)
        //4.2 CAS(Compare And Set) 写的时候对比和自己读的时候是否一样
        //每个实例的内存地址 有一个64为的标志 包括什么什么么锁  标志位等等
        //Doug Lea 道格李  李狗
        //JUC java.utils.concurrent
        //AtomicInteger i=new AtomicInteger();
    }
    public static void thread(){
        //两个方法(没法抛出异常,不能返回值)
//1.直接继承 Thread类   重写run方法 调用自己的start
        //编码简单  但是不能继承其他类
//2.实现Runnable接口 实现run方法 作为参数传给new Thread();
        //编码复杂 但是能继承更多的接口 多个线程能共享一个任务 这个方法使用的更多
//        1.定义内部类 2.定义匿名内部类3.Lambda表达式
//        getState();
//        Thread.currentThread().setName(); //设置线程名字
//        Thread.currentThread().setPriority();//设置线程优先级
//        .......
//实现Callable<>接口 实现call方法
//给FutuerTask FutureTask给Thread
//        task.get()获取线程的返回值 得不到返回值就会阻塞

        //生命周期
        //新建--start()--就绪
        //运行--sleep()/join()/wait()--阻塞--yield()--运行
        //阻塞--sleep()到/join()结束/notify()/notifyAll()/resume()--就绪
        Thread thread=new Thread(()->{
            for(int i=0;i<20;i++)
                System.out.println("lambda");
        });
        thread.start();
//        thread.join();//必须在start之后 让其他进程阻塞 等自己结束之后其他进程就绪 异步线程变成同步的
//        thread.wait();//释放锁 (必须拿到锁之后)进入阻塞 直到notify/notifyAll唤醒
//        thread.sleep();//线程阻塞 但是不会释放锁
//        TimeUnit.SECONDS.sleep(2);
//        thread.yield();//使同优先级或更高优先级的线程执行(自己转为就绪状态)  否则自己执行
//        thread.notify();  --通信
//        thread.notifyAll();
//        thread.setDaemon(); //设置为守护线程 用户线程结束后 守护线程也结束(可能会有延迟)
//        thread.interrupt(); //中断线程 while(thread.isInterrupted()) 可以打断sleep() 被打断之后会自动还原为未打断

    }
}
